package Client;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferStrategy;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.LinkedList;
import javax.swing.JFrame;
import javax.swing.event.MouseInputListener;

public final class GameCanvas extends Canvas implements KeyListener, MouseInputListener {
    public static final Color MENU_BROWN = new Color(93, 84, 71);
    public static final Color MENU_SELECTED_BROWN = new Color(52, 47, 39);
    public static final String VALID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ1234567890!@#$%^&*()_-+={}[]:;'<>,.?/ ";
    public static final int PROCESS_RATE = 20;
    public static final int CANVAS_WIDTH = 765;
    public static final int CANVAS_HEIGHT = 503;
    public static final int TILE_SIZE = 75;
    public static final int MY_PLAYER_TILE_OFFSET_X = 3 * TILE_SIZE;
    public static final int MY_PLAYER_TILE_OFFSET_Y = 2 * TILE_SIZE;
    private static GameCanvas gameCanvas;
    private static JFrame container;
    private static BufferStrategy buffer;
    private static Point2D mouseClick = new Point(-1, -1);
    private static boolean rightClick;
    private static Point2D mousePosition = new Point(-1, -1);
    private static Socket socket;
    private static ByteStream inStream = new ByteStream(5000);
    private static ByteStream outStream = new ByteStream(5000);
    private static InputStream in;
    private static OutputStream out;
    private static Player myPlayer = new Player();
    private static boolean loggedIn;
    private static String username = "";
    private static String password;
    private static String serverIP = "localhost";
    private static int loginBox;
    private static String connectMessage;
    private static int[] interfaceTabs = new int[14];
    private static int viewingTabId;
    private static LinkedList<Character> keysTyped = new LinkedList<Character>();
    private static String savedKeys;
    private static LinkedList<Player> players = new LinkedList<Player>();
    private static int nextPing;
    private static LinkedList<String> chatboxMessages = new LinkedList<String>();
    private static LinkedList<MenuOption> menuOptions = new LinkedList<MenuOption>();
    private static Point2D menuClick = new Point(-1, -1);
    private static Point2D menuTile = new Point(-1, -1);
    private static Point2D menuSize = new Point(0, 0);

    public GameCanvas() {
        setDefaults();
        container = new JFrame("Palidino76");
        container.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        container.setResizable(false);
        container.setVisible(true);
        container.getContentPane().add(this);
        Insets insets = container.getInsets();
        container.setSize(CANVAS_WIDTH + insets.left + insets.right, CANVAS_HEIGHT + insets.top + insets.bottom);
        setIgnoreRepaint(true);
        createBufferStrategy(2);
        buffer = getBufferStrategy();
        addKeyListener(this);
        requestFocus();
        addMouseListener(this);
        addMouseMotionListener(this);
        ObjectDef.unpackConfig();
    }

    public void tick() {
        while (true) {
            long startTick = System.currentTimeMillis();
            handleKeysTyped();
            handleMouse();
            if (loggedIn) {
                Packets.readIncomingPackets();
                myPlayer.tick();
                for (Player p : players) {
                    p.tick();
                }
                if (--nextPing == 0) {
                    nextPing = 5000 / PROCESS_RATE;
                    outStream.addOpcode(0); // Ping - keep the socket alive
                }
                if (outStream.offset() > 0) {
                    try {
                        out.write(outStream.buffer(), 0, outStream.offset());
                    } catch (IOException ioe) {
                        logout();
                    }
                    outStream.setOffset(0);
                }
            }
            paintWindow();
            int sleepTime = PROCESS_RATE - (int) (System.currentTimeMillis() - startTick);
            if (sleepTime > 0) {
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

    public void paintWindow() {
        Graphics2D g = (Graphics2D) buffer.getDrawGraphics();
        g.setColor(Color.BLACK);
        g.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
        if (loggedIn) {
            BufferedImage map = getMap();
            g.drawImage(map, 5, 4, this);
            g.drawImage(Sprite.resize(map, 148, 151), 573, 9, this);
            g.drawImage(Sprite.getSprite(1).image(), 0, 0, this);
            g.setColor(Color.BLACK);
            g.drawLine(14, 453, 506, 453);
            g.setColor(Color.WHITE);
            g.drawString(myPlayer.username() + ": " + savedKeys, 13, 466);
            int chatMessagesY = 346;
            for (String s : chatboxMessages) {
                Rectangle2D r = g.getFont().getStringBounds(colorFilter(s), g.getFontRenderContext());
                chatMessagesY += (int) r.getHeight() + 2;
                if (chatMessagesY >= 453) {
                    break;
                }
                drawString(s, g, 13, chatMessagesY);
            }
            if (interfaceTabs[viewingTabId] != -1) {
                int x = 550;
                int y = 209;
                GameInterface gInterface = GameInterface.getInterface(interfaceTabs[viewingTabId]);
                for (int i = 0; i < gInterface.sprites().length; i++) {
                    g.drawImage(Sprite.getSprite(gInterface.sprites()[i]).image(), gInterface.spriteCoords()[i][0] + x,
                        gInterface.spriteCoords()[i][1] + y, this);
                }
                for (int i = 0; i < gInterface.text().length; i++) {
                    drawString(gInterface.text()[i], g, gInterface.textCoords()[i][0] + x, gInterface.textCoords()[i][1] + y);
                }
            }
            if (menuOptions.size() > 0) {
                g.setColor(MENU_BROWN);
                g.fillRect((int) menuClick.getX(), (int) menuClick.getY(), (int) menuSize.getX(), (int) menuSize.getY());
                g.setColor(Color.BLACK);
                g.fillRect((int) menuClick.getX() + 1, (int) menuClick.getY() + 1, (int) menuSize.getX() - 2, 16);
                g.drawRect((int) menuClick.getX() + 1, (int) menuClick.getY() + 18, (int) menuSize.getX() - 2, (int) menuSize.getY() - 19);
                g.setColor(MENU_BROWN);
                g.drawString("Choose Option", (int) menuClick.getX() + 3, (int) menuClick.getY() + 14);
                int mouseX = (int) mousePosition.getX();
                int mouseY = (int) mousePosition.getY();
                for (int i = 0; i < menuOptions.size(); i++) {
                    int i2 = (int) menuClick.getY() + 31 + (menuOptions.size() - 1 - i) * 15;
                    if (mouseX > menuClick.getX() && mouseX < menuClick.getX() + menuSize.getX() && mouseY > i2 - 13 && mouseY < i2 + 3) {
                        g.setColor(MENU_SELECTED_BROWN);
                        g.fillRect((int) menuClick.getX() + 1, i2 - 13, (int) menuSize.getX() - 2, 17);
                    }
                    drawString(menuOptions.get(i).option(), g, (int) menuClick.getX() + 3, i2);
                }
            }
        } else {
            g.drawImage(Sprite.getSprite(0).image(), 0, 0, this);
            if (connectMessage.length() != 0) {
                drawString("@yel@" + connectMessage, g, 295, 180);
            }
            drawString("@whi@Username: " + username, g, 295, 215);
            drawString("@whi@Password: " + password, g, 295, 245);
            drawString("@whi@Server IP: " + serverIP, g, 295, 275);
        }
        g.dispose();
        buffer.show();
    }

    public BufferedImage getMap() {
        BufferedImage image = new BufferedImage(510, 335, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        for (GameObject go : WorldController.groundTiles()) {
            int pixelX = go.x() * TILE_SIZE - myPlayer.pixelX() + MY_PLAYER_TILE_OFFSET_X;
            int pixelY = go.y() * TILE_SIZE - myPlayer.pixelY() + MY_PLAYER_TILE_OFFSET_Y;
            if (ObjectDef.getObjectDef(go.id()).spriteId() != -1) {
                g.drawImage(Sprite.getSprite(ObjectDef.getObjectDef(go.id()).spriteId()).image(), pixelX, pixelY, this);
            }
        }
        for (GameObject go : WorldController.gameObjects()) {
            int pixelX = go.x() * TILE_SIZE - myPlayer.pixelX() + MY_PLAYER_TILE_OFFSET_X;
            int pixelY = go.y() * TILE_SIZE - myPlayer.pixelY() + MY_PLAYER_TILE_OFFSET_Y;
            if (ObjectDef.getObjectDef(go.id()).spriteId() != -1) {
                g.drawImage(Sprite.getSprite(ObjectDef.getObjectDef(go.id()).spriteId()).image(), pixelX, pixelY, this);
            }
        }
        for (Player p : players) {
            int pixelX = p.pixelX() - myPlayer.pixelX() + MY_PLAYER_TILE_OFFSET_X;
            int pixelY = p.pixelY() - myPlayer.pixelY() + MY_PLAYER_TILE_OFFSET_Y;
            if (p.chat() != null) {
                Rectangle2D r = g.getFont().getStringBounds(colorFilter(p.chat()), g.getFontRenderContext());
                drawString("@yel@" + p.chat(), g, (TILE_SIZE - (int) r.getWidth()) / 2 + pixelX, pixelY - 10);
            }
            g.drawImage(p.appearance().image(), pixelX, pixelY - 10, this);
        }
        if (myPlayer.chat() != null) {
            Rectangle2D r = g.getFont().getStringBounds(colorFilter(myPlayer.chat()), g.getFontRenderContext());
            drawString("@yel@" + myPlayer.chat(), g, (TILE_SIZE - (int) r.getWidth()) / 2 + MY_PLAYER_TILE_OFFSET_X, MY_PLAYER_TILE_OFFSET_Y - 10);
        }
        g.drawImage(myPlayer.appearance().image(), MY_PLAYER_TILE_OFFSET_X, MY_PLAYER_TILE_OFFSET_Y - 10, this);
        g.dispose();
        return image;
    }

    @Override
    public void keyPressed(KeyEvent e) { }

    @Override
    public void keyReleased(KeyEvent e) { }

    @Override
    public void keyTyped(KeyEvent e) {
        synchronized (keysTyped) {
            keysTyped.add(e.getKeyChar());
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
        mouseClick.setLocation(e.getX(), e.getY());
        if (!e.isMetaDown()) {
            rightClick = false;
        } else {
            rightClick = true;
        }
    }

    @Override
    public void mouseExited(MouseEvent e) { }

    @Override
    public void mouseReleased(MouseEvent e) { }

    @Override
    public void mouseClicked(MouseEvent e) { }

    @Override
    public void mouseEntered(MouseEvent e) { }

    @Override
    public void mouseDragged(MouseEvent e) { }

    @Override
    public void mouseMoved(MouseEvent e) {
        mousePosition.setLocation(e.getX(), e.getY());
    }

    public static void drawString(String s, Graphics2D g, int x, int y) {
        g.setColor(Color.BLACK);
        for (int i = 0; i < s.toCharArray().length; i++) {
            char c = s.charAt(i);
            if (c == '@' && i + 4 < s.length()) {
                String color = s.substring(i + 1, i + 4);
                if (color.equals("bla")) {
                    g.setColor(Color.BLACK);
                } else if (color.equals("whi")) {
                    g.setColor(Color.WHITE);
                } else if (color.equals("red")) {
                    g.setColor(Color.RED);
                } else if (color.equals("gre")) {
                    g.setColor(Color.GREEN);
                } else if (color.equals("blu")) {
                    g.setColor(Color.BLUE);
                } else if (color.equals("yel")) {
                    g.setColor(Color.YELLOW);
                } else if (color.equals("cya")) {
                    g.setColor(Color.CYAN);
                } else if (color.equals("ora")) {
                    g.setColor(Color.ORANGE);
                } else if (color.equals("pin")) {
                    g.setColor(Color.PINK);
                } else if (color.equals("gra")) {
                    g.setColor(Color.GRAY);
                } else if (color.equals("men")) {
                    g.setColor(MENU_BROWN);
                }
                i += 4;
            } else {
                g.drawString(c + "", x, y);
                x += (int) g.getFont().getStringBounds(c + "", g.getFontRenderContext()).getWidth();
            }
        }
    }

    public static String colorFilter(String s) {
        String s2 = "";
        for (int i = 0; i < s.toCharArray().length; i++) {
            char c = s.charAt(i);
            if (c == '@' && i + 4 < s.length()) {
                i += 4;
            } else {
                s2 += c;
            }
        }
        return s2;
    }

    public static void addChatboxMessage(String s) {
        if (chatboxMessages.size() > 5) {
            chatboxMessages.removeFirst();
        }
        chatboxMessages.addLast(s);
    }

    public static void setDefaults() {
        for (int i = 0; i < interfaceTabs.length; i++) {
            interfaceTabs[i] = -1;
        }
        nextPing = 5000 / PROCESS_RATE;
        viewingTabId = 3;
        players.clear();
        loginBox = 0;
        password = "";
        connectMessage = "";
        keysTyped.clear();
        savedKeys = "";
    }

    public static void handleKeysTyped() {
        if (keysTyped.isEmpty()) {
            return;
        }
        synchronized (keysTyped) {
            for (char c : keysTyped) {
                if (c == KeyEvent.VK_BACK_SPACE) {
                    if (loggedIn) {
                        if (savedKeys.length() > 0) {
                            savedKeys = savedKeys.substring(0, savedKeys.length() - 1);
                        }
                    } else {
                        if (loginBox == 0) {
                            if (username.length() > 0) {
                                username = username.substring(0, username.length() - 1);
                            }
                        } else if (loginBox == 1) {
                            if (password.length() > 0) {
                                password = password.substring(0, password.length() - 1);
                            }
                        } else if (loginBox == 2) {
                            if (serverIP.length() > 0) {
                                serverIP = serverIP.substring(0, serverIP.length() - 1);
                            }
                        }
                    }
                } else if (c == KeyEvent.VK_ENTER) {
                    if (loggedIn) {
                        if (savedKeys.length() > 0) {
                            outStream.addOpcodeVarByte(2);
                            outStream.addString(savedKeys);
                            outStream.endOpcodeVarByte();
                            savedKeys = "";
                        }
                    } else {
                        if (loginBox == 0 || loginBox == 1) {
                            loginBox++;
                        } else if (loginBox == 2) {
                            login();
                        }
                    }
                } else {
                    if (!VALID_CHARS.contains(Character.toString(c))) {
                        continue;
                    }
                    if (loggedIn) {
                        Graphics2D g = (Graphics2D) buffer.getDrawGraphics();
                        Rectangle2D r = g.getFont().getStringBounds(myPlayer.username() + ": " + savedKeys + c, g.getFontRenderContext());
                        if (r.getWidth() < 500) {
                            savedKeys += c;
                        }
                        g.dispose();
                    } else {
                        if (loginBox == 0) {
                            if (username.length() < 15) {
                                username += c;
                            }
                        } else if (loginBox == 1) {
                            if (password.length() < 25) {
                                password += c;
                            }
                        } else if (loginBox == 2) {
                            serverIP += c;
                        }
                    }
                }
            }
            keysTyped.clear();
        }
    }

    public static void handleMouse() {
        if (menuOptions.size() > 0) {
            if (mousePosition.getX() < menuClick.getX() - 10 || mousePosition.getX() > menuClick.getX() + menuSize.getX() + 10
                    || mousePosition.getY() < menuClick.getY() - 10 || mousePosition.getY() > menuClick.getY() + menuSize.getY() + 10) {
                menuOptions.clear();
            }
        }
        if (mouseClick.getX() == -1 && mouseClick.getY() == -1) {
            return;
        }
        int clickX = (int) mouseClick.getX();
        int clickY = (int) mouseClick.getY();
        if (loggedIn) {
            if (!rightClick) {
                if (clickX < 515 && clickY < 339) {
                    MenuOption menu = null;
                    if (menuOptions.size() > 0) {
                        for (int i1 = 0; i1 < menuOptions.size(); i1++) {
                            int k3 = (int) menuClick.getY() + 31 + (menuOptions.size() - 1 - i1) * 15;
                            if (clickX > menuClick.getX() && clickX < menuClick.getX() + menuSize.getX() && clickY > k3 - 13 && clickY < k3 + 3) {
                                menu = menuOptions.get(i1);
                            }
                        }
                    }
                    if (menu != null) {
                        if (menu.packetId() != -1) {
                            outStream.addOpcodeVarByte(menu.packetId());
                            for (Integer i : menu.write()) {
                                outStream.addShort(i);
                            }
                            outStream.endOpcodeVarByte();
                        }
                        menuOptions.clear();
                    }
                    if (menu == null || menu.clickType() != -1) {
                        int x = clickX / TILE_SIZE - MY_PLAYER_TILE_OFFSET_X / TILE_SIZE;
                        int y = clickY / TILE_SIZE - MY_PLAYER_TILE_OFFSET_Y / TILE_SIZE;
                        if (menu != null) {
                            x = (int) menuTile.getX() - myPlayer.x();
                            y = (int) menuTile.getY() - myPlayer.y();
                        }
                        outStream.addOpcode(1);
                        outStream.addByte(x);
                        outStream.addByte(y);
                        outStream.addByte(menu != null ? menu.clickType() : 0);
                    }
                } else if (clickX >= 573 && clickX <= 721 && clickY >= 9 && clickY <= 160) {
                    outStream.addOpcode(1);
                    outStream.addByte(clickX / 33 - MY_PLAYER_TILE_OFFSET_X / TILE_SIZE);
                    outStream.addByte(clickY / 33 - MY_PLAYER_TILE_OFFSET_Y / TILE_SIZE);
                } else if (clickX >= 523 && clickY >= 168 && clickX <= 558 && clickY <= 201) {
                    viewingTabId = 0;
                } else if (clickX >= 561 && clickY >= 168 && clickX <= 591 && clickY <= 201) {
                    viewingTabId = 1;
                } else if (clickX >= 594 && clickY >= 168 && clickX <= 624 && clickY <= 201) {
                    viewingTabId = 2;
                } else if (clickX >= 627 && clickY >= 168 && clickX <= 657 && clickY <= 201) {
                    viewingTabId = 3;
                } else if (clickX >= 660 && clickY >= 168 && clickX <= 690 && clickY <= 201) {
                    viewingTabId = 4;
                } else if (clickX >= 693 && clickY >= 168 && clickX <= 723 && clickY <= 201) {
                    viewingTabId = 5;
                } else if (clickX >= 726 && clickY >= 168 && clickX <= 761 && clickY <= 201) {
                    viewingTabId = 6;
                } else if (clickX >= 523 && clickY >= 466 && clickX <= 558 && clickY <= 499) {
                    viewingTabId = 7;
                } else if (clickX >= 561 && clickY >= 466 && clickX <= 591 && clickY <= 499) {
                    viewingTabId = 8;
                } else if (clickX >= 594 && clickY >= 466 && clickX <= 624 && clickY <= 499) {
                    viewingTabId = 9;
                } else if (clickX >= 627 && clickY >= 466 && clickX <= 657 && clickY <= 499) {
                    viewingTabId = 10;
                } else if (clickX >= 660 && clickY >= 466 && clickX <= 690 && clickY <= 499) {
                    viewingTabId = 11;
                } else if (clickX >= 693 && clickY >= 466 && clickX <= 723 && clickY <= 499) {
                    viewingTabId = 12;
                } else if (clickX >= 726 && clickY >= 466 && clickX <= 761 && clickY <= 499) {
                    viewingTabId = 13;
                }
            } else {
                if (clickX < 515 && clickY < 339) {
                    menuClick.setLocation(clickX, clickY);
                    menuOptions.clear();
                    menuOptions.add(new MenuOption("@whi@Cancel", -1, -1));
                    menuOptions.add(new MenuOption("@whi@Walk here", -1, 0));
                    int tileX = myPlayer.x() + clickX / TILE_SIZE - MY_PLAYER_TILE_OFFSET_X / TILE_SIZE;
                    int tileY = myPlayer.y() + clickY / TILE_SIZE - MY_PLAYER_TILE_OFFSET_Y / TILE_SIZE;
                    menuTile.setLocation(tileX, tileY);
                    for (GameObject go : WorldController.gameObjects()) {
                        if (go.x() == tileX && go.y() == tileY) {
                            ObjectDef def = ObjectDef.getObjectDef(go.id());
                            if (def.option2() != null) {
                                menuOptions.add(new MenuOption("@whi@" + def.option2() + " @cya@" + def.name(), -1, 1));
                            }
                            if (def.option1() != null) {
                                menuOptions.add(new MenuOption("@whi@" + def.option1() + " @cya@" + def.name(), -1, 1));
                            }
                        }
                    }
                    Graphics2D g = (Graphics2D) buffer.getDrawGraphics();
                    int width = (int) g.getFont().getStringBounds("Choose Option", g.getFontRenderContext()).getWidth();
                    for (MenuOption menu : menuOptions) {
                        int k = (int) g.getFont().getStringBounds(colorFilter(menu.option()), g.getFontRenderContext()).getWidth();
                        if (k > width) {
                            width = k;
                        }
                    }
                    width += 8;
                    menuSize.setLocation(width, 15 * menuOptions.size() + 21);
                    g.dispose();
                }
            }
        } else {
            if (clickX >= 295 && clickY >= 205 && clickX <= 480 && clickY <= 215) {
                loginBox = 0;
            } else if (clickX >= 295 && clickY >= 235 && clickX <= 471 && clickY <= 245) {
                loginBox = 1;
            } else if (clickX >= 295 && clickY >= 265 && clickX <= 471 && clickY <= 275) {
                loginBox = 2;
            } else if (clickX >= 295 && clickY >= 288 && clickX <= 471 && clickY <= 343) {
                login();
            }
        }
        mouseClick.setLocation(-1, -1);
    }

    public static void login() {
        if (username.length() == 0 || password.length() == 0) {
            return;
        }
        connectMessage = "Connecting to server...";
        gameCanvas.paintWindow();
        try {
            socket = new Socket(serverIP, 43594);
            out = socket.getOutputStream();
            in = socket.getInputStream();
            socket.setSoTimeout(30000);
            socket.setTcpNoDelay(true);
            out = socket.getOutputStream();
            outStream.setOffset(0);
            outStream.addByte(14);
            outStream.addInt(1);
            outStream.addString(username);
            outStream.addString(password);
            out.write(outStream.buffer(), 0, outStream.offset());
            outStream.setOffset(0);
            in.read(inStream.buffer(), 0, 3);
            inStream.setLength(3);
            int loginResponse = inStream.getUnsignedByte();
            int myPlayerId = inStream.getUnsignedShort();
            if (loginResponse == 2) {
                myPlayer.setId(myPlayerId);
                myPlayer.setUsername(username);
                loggedIn = true;
            } else {
                connectMessage = "Server rejected the connection.";
            }
        } catch (IOException ioe) {
            connectMessage = "Error connecting to server.";
        }
        outStream.setOffset(0);
        inStream.setOffset(0);
        inStream.setLength(0);
    }

    public static void logout() {
        loggedIn = false;
        setDefaults();
        try {
            if (socket != null) {
                socket.close();
                socket = null;
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    public static Socket socket() {
        return socket;
    }

    public static Player myPlayer() {
        return myPlayer;
    }

    public static LinkedList<Player> players() {
        return players;
    }

    public static ByteStream inStream() {
        return inStream;
    }

    public static InputStream in() {
        return in;
    }

    public static void main(String[] args) {
        gameCanvas = new GameCanvas();
        gameCanvas.tick();
    }
}